//
// Sidebar.cs
//
// Copyright (C) 2OO8 Lukas Lipka <lukaslipka@gmail.com>
//

using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;

using Dashboard;
using Dashboard.Util;
using Dashboard.Engine;
using Dashboard.Mint.Tiles;
using Dashboard.Mint.Widgets;

using Gtk;
using Gdk;

namespace Dashboard.Sidebar
{
	public class Sidebar : Gtk.Window
	{
		private TilePage tile_page = null;
		private List<Tile> tiles = new List<Tile> ();

		private VBox notification_area = null;		

		private MessageBroker message_broker = null;

		public Sidebar () : base  ("Dashboard")
		{
			base.DefaultWidth = 250;
			base.DefaultHeight = 750;
			base.BorderWidth = 5;

			VBox vbox = new VBox (false, 5);

			tile_page = new TilePage ();
			vbox.PackStart (tile_page, true, true, 0);

			notification_area = new VBox (false, 5);
			vbox.PackEnd (notification_area, false, true, 0);

			// FIXME: Yeah, that naming isn't that great.
			message_broker = Dashboard.Engine.Engine.MessageBroker;
			message_broker.Subscribe ("/event/*", typeof (ClueMessage), OnClueMessage);
			message_broker.Subscribe ("/event/match/found", typeof (MatchMessage), OnMatchMessage);
			message_broker.Subscribe ("/event/notification/message", typeof (NotificationMessage), OnNotificationMessage);

			Add (vbox);
		}

		private void PushAlphaVersionNotification ()
		{
			InformationBar bar = new InformationBar ();
			bar.Icon = Dashboard.Mint.Util.LoadStockIcon (Stock.DialogInfo, 32);
			bar.Title = "Dashboard " + ConfigureDefines.Version;
			bar.Text = "Welcome to the first preview version of Dashboard. Keep in " +
				"mind that this version is still under heavy development and " +
				"may not work and behave as expected. However, please do report " +
				"any difficulties or bugs that you experience while using it.";
			bar.AddAction ("Hide", delegate { bar.Hide (); });

			AnimatedWidget animation = new AnimatedWidget (1500);
			animation.Add (bar);
			animation.ShowAll ();
			
			notification_area.PackStart (animation, false, true, 0);

			animation.Grow ();
		}

		protected override void OnShown ()
		{
			base.OnShown ();

			PushAlphaVersionNotification ();
		}

		private void OnClueMessage (Message message)
		{
			// Save the clue message and window id 
			// so that we can map matches to the right 
			// workspace later.
			
			ClueMessage clue_message = message as ClueMessage;
			//windows_by_clue_message_id [clue_message.Id] = clue_message.Window;

			// If the replaces property is set, we remove all 
			// tiles that are matched to the clue that we're
			// replacing.
			
			if (!String.IsNullOrEmpty (clue_message.Replaces)) {
				List<Tile> tiles_to_remove = GetTilesByClueMessageId (clue_message.Replaces);
				// FIXME: Don't assume it's the same window
				// as the clue message it's replacing.
				//TilePage tile_page = GetTilePage (Wnck.Window.Get (clue_message.Window).Workspace.Number.ToString ());
				
				foreach (Tile tile in tiles_to_remove) {
					tiles.Remove (tile);
					tile_page.RemoveTile (tile);
				}
			}
		}

		private void OnMatchMessage (Message message)
		{
			MatchMessage match_message = message as MatchMessage;
	
			// This gets invoked in it's own thread, so
			// proxy it to the main thread.

			ThreadAssist.ProxyToMain (delegate {
				foreach (Match match in match_message.Matches) {
					//if (!windows_by_clue_message_id.ContainsKey (match.Trigger))
					//	continue;

					//Wnck.Window window = Wnck.Window.Get (windows_by_clue_message_id [match.Trigger]);
					
					Tile tile = TileFactory.BuildTile (match);

					if (tile == null)
						continue;

					//tile.Window = window.Xid;
					tiles.Add (tile);

					//TilePage tile_page = GetTilePage (window.Workspace.Number.ToString ());
					tile_page.AddTile (tile);				
					//tile_page.Show ();

					//if (window.Workspace.Number == screen.ActiveWorkspace.Number)
					//	FocusTilePage (window.Workspace.Number.ToString ());
				}
			});
		}

		private void OnNotificationMessage (Message message)
		{
			NotificationMessage notification = message as NotificationMessage;

			if (notification == null)
				return;

			InformationBar bar = new InformationBar ();
			bar.Title = notification.Title;
			bar.Text = notification.Text;

			if (!String.IsNullOrEmpty (notification.Icon))
				bar.Icon = Dashboard.Mint.Util.LoadStockIcon (notification.Icon, 32);

			bar.ShowAll ();

			AnimatedWidget animation = new AnimatedWidget (1500);
			animation.Add (bar);
			animation.ShowAll ();
			
			notification_area.PackStart (animation, false, true, 0);

			animation.Grow ();
		}

		/*private void OnActiveWindowChanged (object o, EventArgs args)
		{
			// FIXME: Elevate relevancy?
		}

		private void OnActiveWorkspaceChanged (object o, EventArgs args)
		{
			// Switch to the correct notebook page
			// when we switch workspace.

			Wnck.Workspace workspace = screen.ActiveWorkspace;
			FocusTilePage (workspace.Number.ToString ());
		}
		
		private void OnWindowClosed (object o, Wnck.WindowClosedArgs args)
		{
			// Remove all matches that relates to
			// the windows being closed.

			Wnck.Window window = args.Window;
			
			if (window == null || window.Workspace == null)
				return;

			TilePage tile_page = GetTilePage (window.Workspace.Number.ToString ());

			if (tile_page == null)
				return;

			List<Tile> tiles_to_remove = GetTilesByXid (window.Xid);
			
			foreach (Tile tile in tiles_to_remove) {
				tiles.Remove (tile);

				if (tile_page.ContainsTile (tile))
					tile_page.RemoveTile (tile);
			}
		}*/

		// FIXME: Use a sorted list for the tiles array

		private List<Tile> GetTilesByXid (ulong xid)
		{
			List<Tile> found = new List<Tile> ();

			foreach (Tile tile in tiles) {
				if (tile.Window == xid)
					found.Add (tile);
			}

			return found;
		}

		private List<Tile> GetTilesByClueMessageId (string id)
		{
			List<Tile> found = new List<Tile> ();

			foreach (Tile tile in tiles) {
				if (tile.Match.Trigger == id)
					found.Add (tile);
			}

			return found;			
		}
	}
}
